using System;
using System.Collections.Generic;
using System.Linq;
using FluentIoC.Helpers;
using FluentIoC.Interfaces;

namespace FluentIoC.Configuration.Commands
{
    public class RegisterService : RegisterProperties, IRegistration
    {
        internal Type ServiceType { get; set; }
        internal Type ComponentType { get; set; }
        internal string ServiceKey { get; set; }

        public Dictionary<Type, string> Services
        {
            get
            {
                return new Dictionary<Type, string> { { ServiceType, ServiceKey } };
            }
        }

        
        private List<IRegistration> _internalRegistrations = new List<IRegistration>();
       
        private bool _serviceIsImplementation
        {
            get { return ComponentType == null && ServiceType.IsClass; }
        }

        public RegisterService(Type serviceType)
            : this(serviceType, serviceType.GetDefaultKey())
        {
        }

        public RegisterService(Type serviceType, string serviceKey)
        {
            ServiceType = serviceType;
            ServiceKey = serviceKey;
        }

        public RegisterService(Type serviceType, string serviceKey, Type componentType)
        {
            ServiceType = serviceType;
            ServiceKey = serviceKey;
            ComponentType = componentType;
        }

        public RegisterService ImplementedBy<T>()
        {
            return ImplementedBy(typeof (T));
        }

        public RegisterService ImplementedBy(Type componentType)
        {
            if(false == componentType.Implements(ServiceType))
            {
                throw new FluentIoCException(
                    "The component {0} does not implement the service {1}", componentType.Name, ServiceType.Name
                );
            }

            ComponentType = componentType;
            return this;
        }

        public RegisterService UsingFactory<T>(Func<T, object> factoryCreateMethod)
        {
            _internalRegistrations.Add(
                new RegisterFactory<T>(
                    factoryCreateMethod
                )
            );

            return this;
        }

        public RegisterService UsingFactory<T>(string factoryCreateMethod)
        {
            _internalRegistrations.Add(
                new RegisterFactory(
                    typeof (T), 
                    typeof (T).GetDefaultKey(), 
                    factoryCreateMethod
                )
            );

            return this;
        }

        private RegisterFactory GetFactoryRegistration(IEnumerable<IRegistration> registrations)
        {
            var factoryRegistrations = registrations.Where(r => r is RegisterFactory).ToArray();

            switch(factoryRegistrations.Length)
            {
                case 0:
                    return new RegisterFactory(null, string.Empty);
                case 1:
                    return factoryRegistrations[0] as RegisterFactory;
                default:
                    throw new FluentIoCException(
                        "The service {0} can only have 1 factory (Currently implemented by {1}", ServiceType, factoryRegistrations.Length
                    );
            }
        }

        public void Configure(IIoCConfiguration config, Dictionary<Type, string> services)
        {
            if(_serviceIsImplementation)
            {
                config.RegisterComponent(ServiceKey, ServiceType);
            }
            else
            {
                if (null == ComponentType)
                {
                    throw new FluentIoCException("The service {0} must have an implementing type", ServiceType.Name);
                }

                //Configure all of the properties
                _setProperties.ForEach(p => p.Configure(services));

                var factory = GetFactoryRegistration(_internalRegistrations);

                if (null != factory.FactoryType)
                {
                    config.RegisterFactory(
                        factory.Key,
                        factory.FactoryType
                    );
                }

                config.RegisterService(
                    ServiceKey, 
                    ServiceType, 
                    ComponentType, 
                    Properties, 
                    Lifestyle, 
                    factory.FactoryType,
                    factory.CreateMethod
                );
            }
        }

        public bool IsValid(IConventions conventions)
        {
            _internalRegistrations = _internalRegistrations.Where(r => r.IsValid(conventions)).ToList();

            if(false == conventions.IsValidType(ServiceType))
            {
                Debug.FilteredInvalidType(ServiceType, ConventionInvalidReason.InvalidType);
                
                return false;
            }

            if (false == conventions.IsValidType(ComponentType))
            {
                Debug.FilteredInvalidType(ComponentType, ConventionInvalidReason.InvalidType);

                return false;
            }

            if (false == conventions.IsValidServiceType(ServiceType))
            {
                Debug.FilteredInvalidType(ServiceType, ConventionInvalidReason.InvalidService);

                return false;
            }

            if (null != ComponentType && false == conventions.IsValidComponentType(ComponentType))
            {
                Debug.FilteredInvalidType(ComponentType, ConventionInvalidReason.InvalidComponent);

                return false;
            }

            return true;
        }
    }
}